﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.ComponentModel;

using SharpEnviro.Interop;
using SharpEnviro.Interop.Enums;
using SharpEnviro.Interop.Structs;

namespace SharpEnviro.Desktop
{
	/// <summary>
	/// Interaction logic for Main.xaml
	/// </summary>
	public partial class Main : Window
	{
		/// <summary>
		/// The main window.
		/// </summary>
		public Main()
		{
			InitializeComponent();

			// This is a little trick to get windows to think the window is not transparent.
			// Windows treats fully transparent layered-windows windows as hollow in that you can click through it.
			canvas1.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0));

			Top = 0;
			Left = 0;

			//TODO: Move code to better place???

			// Set the form height and width.
			Height = Screen.AllScreens[0].Bounds.Height;
			Width = Screen.AllScreens[0].Bounds.Width;

			FileSystemWatcher watcher = new FileSystemWatcher(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory));

			watcher.Changed += new FileSystemEventHandler(
				delegate(object Sender, FileSystemEventArgs e)
				{
				});
			watcher.Created += new FileSystemEventHandler(
				delegate(object Sender, FileSystemEventArgs e)
				{
					if (Thread.CurrentThread != Dispatcher.Thread)
						Dispatcher.Invoke(DispatcherPriority.Normal,
							(ThreadStart)delegate
							{
								AddFile(e.FullPath);
							});
				});
			watcher.Deleted += new FileSystemEventHandler(
				delegate(object Sender, FileSystemEventArgs e)
				{
					if (Thread.CurrentThread != Dispatcher.Thread)
						Dispatcher.Invoke(DispatcherPriority.Normal,
							(ThreadStart)delegate
							{
								RemoveObject(e.FullPath);
							});
				});
			watcher.Renamed += new RenamedEventHandler(
				delegate(object Sender, RenamedEventArgs e)
				{
					if (Thread.CurrentThread != Dispatcher.Thread)
						Dispatcher.Invoke(DispatcherPriority.Normal,
							(ThreadStart)delegate
							{
								RenameObject(e.OldFullPath, e.FullPath);
							});
				});

			watcher.EnableRaisingEvents = true;

			Refresh();
		}

		private void Window_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
		{
			if (e.Key == Key.Escape)
				Close();
		}

		private void Refresh()
		{
			DirectoryInfo desktop = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory));

			foreach (FileInfo fi in desktop.GetFiles())
			{
				// Do not display hidden files.
				if ((fi.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
					continue;

				AddFile(fi.FullName);
			}

			foreach (DirectoryInfo di in desktop.GetDirectories())
			{
				// Do not display hidden directories.
				if ((di.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
					continue;

				AddDirectory(di.FullName);
			}
		}

		private void AddFile(string file)
		{
			DesktopObject desktopFile = new DesktopObject(file, DesktopObjectType.File);
			desktopFile.MaxHeight = 110;
			desktopFile.MaxWidth = 78;
			desktopFile.VerticalAlignment = VerticalAlignment.Center;
			desktopFile.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
			desktopFile.SetValue(Canvas.LeftProperty, 10.0);
			desktopFile.SetValue(Canvas.TopProperty, 64.0);
			canvas1.Children.Add(desktopFile);
		}

		private void AddDirectory(string directory)
		{
			DesktopObject desktopDirectory = new DesktopObject(directory, DesktopObjectType.Directory);
			desktopDirectory.MaxHeight = 96 + 39;
			desktopDirectory.MaxWidth = 96 + 14;
			desktopDirectory.VerticalAlignment = VerticalAlignment.Center;
			desktopDirectory.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
			desktopDirectory.SetValue(Canvas.LeftProperty, 100.0);
			desktopDirectory.SetValue(Canvas.TopProperty, 64.0);
			canvas1.Children.Add(desktopDirectory);
		}

		private void RemoveObject(string path)
		{
			foreach (DesktopObject desktopFile in canvas1.Children)
			{
				if (desktopFile.Settings.Path == path)
				{
					canvas1.Children.Remove(desktopFile);
					return;
				}
			}
		}

		private void RenameObject(string oldPath, string newPath)
		{
			foreach (DesktopObject desktopObj in canvas1.Children)
			{
				if (desktopObj.Settings.Path == oldPath)
				{
					desktopObj.Settings.Rename(newPath);
					return;
				}
			}
		}

		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
			//return;
			// Remove the window from being displayed when user does Alt+Tab;
			WindowInteropHelper windowHelper = new WindowInteropHelper(this);

			int style = (int)PInvoke.GetWindowLongPtr(windowHelper.Handle, (int)GWL.EXSTYLE);

			if (style == 0)
			{
				throw new Win32Exception(Marshal.GetLastWin32Error());
			}

			style |= (int)WindowStylesExtended.WS_EX_TOOLWINDOW;

			// Clear the last error before calling SetWindowLongPtr because it does not clear it.
			PInvoke.SetLastError(0);

			int result = (int)PInvoke.SetWindowLongPtr(windowHelper.Handle, (int)GWL.EXSTYLE, (IntPtr)style);

			if (result == 0)
			{
				throw new Win32Exception(Marshal.GetLastWin32Error());
			}

			// Set the window to the bottom and don't allow it to be moved or sized.
			if (!PInvoke.SetWindowPos(windowHelper.Handle, PInvoke.HWND_BOTTOM, 0, 0, 0, 0, (uint)(SWP.NOACTIVATE | SWP.NOMOVE | SWP.NOSIZE)))
			{
				throw new Win32Exception(Marshal.GetLastWin32Error());
			}

			// Hook the window procedure so we can handle certain messages.
			HwndSource.FromHwnd(windowHelper.Handle).AddHook(new HwndSourceHook(WindowProc));
		}

		/// <summary>
		/// The window procedure for the window.
		/// </summary>
		/// <param name="hWnd">The window handle.</param>
		/// <param name="msg">The message ID.</param>
		/// <param name="wParam">The message's wParam value.</param>
		/// <param name="lParam">The message's lParam value.</param>
		/// <param name="handled">
		/// A value that indicates the message was handled.
		/// Set the value to true if the message was handled, otherwise false.
		/// </param>
		/// <returns>
		/// The appropriate return value depends on the particular message.
		/// See the message documentation details for the Win32 message being handled.
		/// </returns>
		public static IntPtr WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
		{
			//return new IntPtr(0);
			if (msg == (int)WM.WINDOWPOSCHANGING)
			{
				WindowPos pos = (WindowPos)Marshal.PtrToStructure(lParam, typeof(WindowPos));

				pos.Flags |= (uint)SWP.NOZORDER;

				Marshal.StructureToPtr(pos, lParam, true);

				handled = true;
			}

			return new IntPtr(0);
		}

		private void Window_Unloaded(object sender, RoutedEventArgs e)
		{
			// Unregister the window procedure hook we created in Window_Loaded.
			WindowInteropHelper windowHelper = new WindowInteropHelper(this);
			HwndSource.FromHwnd(windowHelper.Handle).RemoveHook(new HwndSourceHook(WindowProc));
		}

		private void canvas1_MouseDown(object sender, MouseButtonEventArgs e)
		{
			// We enable the canvas to focusable to that changing the focus to it will actually work.
			// We then disable the canvas from being focusable so that it is not in the tab sequence.
			canvas1.Focusable = true;
			canvas1.Focus();
			canvas1.Focusable = false;
		}

		private void canvas1_DragEnter(object sender, System.Windows.DragEventArgs e)
		{
			if (e.Data.GetDataPresent(typeof(DesktopObject)))
				e.Effects = System.Windows.DragDropEffects.Move;
			else
				e.Effects = System.Windows.DragDropEffects.None;
		}

		private void canvas1_Drop(object sender, System.Windows.DragEventArgs e)
		{
			if ((e.Effects & System.Windows.DragDropEffects.Move) == System.Windows.DragDropEffects.Move)
			{
				DesktopObject desktopObj = (DesktopObject)e.Data.GetData(typeof(DesktopObject));
				Point dropPoint = e.GetPosition(canvas1);
				desktopObj.SetValue(Canvas.LeftProperty, dropPoint.X);
				desktopObj.SetValue(Canvas.TopProperty, dropPoint.Y);
			}
		}
	}
}
